+ /**
+ * Constructor.
+ *
+ * FIXME: It is possible to construct a Database object with no associated
+ * connection object, by specifying no parameters to __construct(). This
+ * feature is deprecated and should be removed.
+ *
+ * IDatabase classes should not be constructed directly in external
+ * code. DatabaseBase::factory() should be used instead.
+ *
+ * @param array $params Parameters passed from DatabaseBase::factory()
+ */
+ function __construct( array $params ) {
+ $server = $params['host'];
+ $user = $params['user'];
+ $password = $params['password'];
+ $dbName = $params['dbname'];
+ $flags = $params['flags'];
+
+ $this->mSchema = $params['schema'];
+ $this->mTablePrefix = $params['tablePrefix'];
+
+ $this->cliMode = isset( $params['cliMode'] )
+ ? $params['cliMode']
+ : ( PHP_SAPI === 'cli' );
+
+ $this->mFlags = $flags;
+ if ( $this->mFlags & DBO_DEFAULT ) {
+ if ( $this->cliMode ) {
+ $this->mFlags &= ~DBO_TRX;
+ } else {
+ $this->mFlags |= DBO_TRX;
+ }
+ }
+
+ $this->mSessionVars = $params['variables'];
+
+ $this->srvCache = isset( $params['srvCache'] )
+ ? $params['srvCache']
+ : new HashBagOStuff();
+
+ $this->profiler = isset( $params['profiler'] )
+ ? $params['profiler']
+ : Profiler::instance(); // @TODO: remove global state
+ $this->trxProfiler = isset( $params['trxProfiler'] )
+ ? $params['trxProfiler']
+ : new TransactionProfiler();
+ $this->connLogger = isset( $params['connLogger'] )
+ ? $params['connLogger']
+ : new \Psr\Log\NullLogger();
+ $this->queryLogger = isset( $params['queryLogger'] )
+ ? $params['queryLogger']
+ : new \Psr\Log\NullLogger();
+
+ if ( $user ) {
+ $this->open( $server, $user, $password, $dbName );
+ }
+ }
+
+ /**
+ * Given a DB type, construct the name of the appropriate child class of
+ * IDatabase. This is designed to replace all of the manual stuff like:
+ * $class = 'Database' . ucfirst( strtolower( $dbType ) );
+ * as well as validate against the canonical list of DB types we have
+ *
+ * This factory function is mostly useful for when you need to connect to a
+ * database other than the MediaWiki default (such as for external auth,
+ * an extension, et cetera). Do not use this to connect to the MediaWiki
+ * database. Example uses in core:
+ * @see LoadBalancer::reallyOpenConnection()
+ * @see ForeignDBRepo::getMasterDB()
+ * @see WebInstallerDBConnect::execute()
+ *
+ * @since 1.18
+ *
+ * @param string $dbType A possible DB type
+ * @param array $p An array of options to pass to the constructor.
+ * Valid options are: host, user, password, dbname, flags, tablePrefix, schema, driver
+ * @return IDatabase|null If the database driver or extension cannot be found
+ * @throws InvalidArgumentException If the database driver or extension cannot be found
+ */
+ final public static function factory( $dbType, $p = [] ) {
+ global $wgCommandLineMode;
+
+ $canonicalDBTypes = [
+ 'mysql' => [ 'mysqli', 'mysql' ],
+ 'postgres' => [],
+ 'sqlite' => [],
+ 'oracle' => [],
+ 'mssql' => [],
+ ];
+
+ $driver = false;
+ $dbType = strtolower( $dbType );
+ if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
+ $possibleDrivers = $canonicalDBTypes[$dbType];
+ if ( !empty( $p['driver'] ) ) {
+ if ( in_array( $p['driver'], $possibleDrivers ) ) {
+ $driver = $p['driver'];
+ } else {
+ throw new InvalidArgumentException( __METHOD__ .
+ " type '$dbType' does not support driver '{$p['driver']}'" );
+ }
+ } else {
+ foreach ( $possibleDrivers as $posDriver ) {
+ if ( extension_loaded( $posDriver ) ) {
+ $driver = $posDriver;
+ break;
+ }
+ }
+ }
+ } else {
+ $driver = $dbType;
+ }
+ if ( $driver === false ) {
+ throw new InvalidArgumentException( __METHOD__ .
+ " no viable database extension found for type '$dbType'" );
+ }
+
+ // Determine schema defaults. Currently Microsoft SQL Server uses $wgDBmwschema,
+ // and everything else doesn't use a schema (e.g. null)
+ // Although postgres and oracle support schemas, we don't use them (yet)
+ // to maintain backwards compatibility
+ $defaultSchemas = [
+ 'mssql' => 'get from global',
+ ];
+
+ $class = 'Database' . ucfirst( $driver );
+ if ( class_exists( $class ) && is_subclass_of( $class, 'IDatabase' ) ) {
+ // Resolve some defaults for b/c
+ $p['host'] = isset( $p['host'] ) ? $p['host'] : false;
+ $p['user'] = isset( $p['user'] ) ? $p['user'] : false;
+ $p['password'] = isset( $p['password'] ) ? $p['password'] : false;
+ $p['dbname'] = isset( $p['dbname'] ) ? $p['dbname'] : false;
+ $p['flags'] = isset( $p['flags'] ) ? $p['flags'] : 0;
+ $p['variables'] = isset( $p['variables'] ) ? $p['variables'] : [];
+ $p['tablePrefix'] = isset( $p['tablePrefix'] ) ? $p['tablePrefix'] : '';
+ if ( !isset( $p['schema'] ) ) {
+ $p['schema'] = isset( $defaultSchemas[$dbType] ) ? $defaultSchemas[$dbType] : null;
+ }
+ $p['foreign'] = isset( $p['foreign'] ) ? $p['foreign'] : false;
+ $p['cliMode'] = $wgCommandLineMode;
+
+ $conn = new $class( $p );
+ if ( isset( $p['connLogger'] ) ) {
+ $conn->connLogger = $p['connLogger'];
+ }
+ if ( isset( $p['queryLogger'] ) ) {
+ $conn->queryLogger = $p['queryLogger'];
+ }
+ if ( isset( $p['errorLogger'] ) ) {
+ $conn->errorLogger = $p['errorLogger'];
+ } else {
+ $conn->errorLogger = [ MWExceptionHandler::class, 'logException' ];
+ }
+ } else {
+ $conn = null;
+ }
+
+ return $conn;
+ }
+
+ public function setLogger( LoggerInterface $logger ) {
+ $this->queryLogger = $logger;
+ }
+